#pragma once
#include <Rz/IObject.h>

#include <type_traits>

namespace Rz {
namespace IsEquatableImpl {

struct NoOperator  { };
struct HasOperator { };

NoOperator  operator , (NoOperator, HasOperator);
HasOperator operator , (bool,       HasOperator);

template <typename T>
struct OperatorExists
{
	typedef char (&YesType)[1];
	typedef char (&NoType) [2];

	template <typename TAny>
	friend NoOperator operator == (const TAny&, const TAny&);

	static YesType Check(HasOperator);
	static NoType  Check(NoOperator);

	static const bool Value = ( sizeof( Check(((std::declval<T>() == std::declval<T>()), std::declval<HasOperator>())) ) == sizeof(YesType) );
};

template <>
struct OperatorExists<void>
{
	static const bool Value = false;
};

}

//////////////////////////////////////////////////////////////////////////

template <typename T>
struct IsEquatable :
	public std::integral_constant<bool, IsEquatableImpl::OperatorExists<T>::Value>
{

};

template <typename T>
class IEquatable :
	virtual public IObject
{
	public:
		RZ_RTTI_INTERFACE(Rz, IEquatable);
		RZ_RTTI_IMPLEMENTS(IObject);
		RZ_RTTI_TEMPLATES(T);
		RZ_RTTI_END;

		virtual bool Equals(const T& r) const = 0;
		virtual bool operator == (const T& r) const = 0;
		virtual bool operator != (const T& r) const = 0;
};

}
